package com.qunar.wan.support.lucene;

import com.qunar.wan.common_api.model.SearchResult;
import com.qunar.wan.common_api.service.ConvertService;
import com.qunar.wan.common_api.service.LuceneSearchService;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.AsyncResult;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Future;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * Created by hiCyoung on 13-12-1.
 * Lucene搜索的基础实现
 */
public abstract class CommonSearcher<T, K> implements LuceneSearchService<T> {
    protected Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * 搜索返回最多记录数
     */
    private final static int MAX_RESULTS_NUM = 300;


    public SearchResult search(String queryString, int pageSize, int curPage) throws Exception {
        checkArgument(pageSize > 0, "pageSize应该大于0");
        long start = System.currentTimeMillis();
        Query query = buildQuery(queryString);
        SearchResult<K> searchResult = new SearchResult();
        IndexSearcher searcher = LuceneContext.getSearcher();
        Sort sort = getSortRules();
        TopDocs results = sort == null ? searcher.search(query, MAX_RESULTS_NUM) : searcher.search(query, MAX_RESULTS_NUM, sort);
        ScoreDoc[] hits = results.scoreDocs;
        int numTotalHits = results.totalHits;
        if (numTotalHits == 0) {
            logger.warn("搜索\"{}\"无结果", query.toString());
            return null;
        }
        searchResult.setTotalPage((numTotalHits - 1) / pageSize + 1);
        int begin = pageSize * (curPage - 1);
        int end = Math.min(begin + pageSize, numTotalHits);
        List<K> list = new LinkedList();
        for (int i = begin; i < end; i++) {
            ScoreDoc scoreDoc = hits[i];
            Document doc = searcher.doc(scoreDoc.doc);
            list.add(getConvertService().convert2IndexObj(doc, query));
        }
        logger.info("搜索\"{}\"第{}页(共{}条记录,每页{}条)耗时:{}ms", query.toString(), curPage, numTotalHits, pageSize,
                System.currentTimeMillis() - start);
        searchResult.setIndexLists(list);
        return searchResult;
    }

    @Override
    public Future<Boolean> addIndexDoc(T t) throws IOException {
        DocumentWriter.addIndexDoc(getConvertService(), t);
        return new AsyncResult<Boolean>(true);
    }

    /**
     * 转换规则
     * <T> 需要转换到Doc filed的对象模型
     * <K>  对应的搜索结果对象模型
     *
     * @return
     */
    public abstract ConvertService<T, K> getConvertService();

    /**
     * 排序规则
     *
     * @return
     */
    public abstract Sort getSortRules();
}
